;Network Interface/Driver for POSx86
;Copyright (c) 2009, 2010 Max Reitz
;
;Permission is hereby granted,  free of charge,  to any  person obtaining a
;copy of this software and associated documentation files (the "Software"),
;to deal in the Software without restriction,  including without limitation
;the rights to use,  copy, modify, merge, publish,  distribute, sublicense,
;and/or sell copies  of the  Software,  and to permit  persons to whom  the
;Software is furnished to do so, subject to the following conditions:
;
;The above copyright notice and this permission notice shall be included in
;all copies or substantial portions of the Software.
;
;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL
;THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING
;FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER
;DEALINGS IN THE SOFTWARE.
IP_DEFINE: 
 ; This code must not be executed.
 ; Define the IP Address of the Device
 ; VERSION : IPV4
 ; 10.0.2.15
 my_ip   dw 0x0F02000A ; Your IP
 ;10.0.2.2 - Good for puke like QEMU
 dest_ip dw 0x0202000A ; Destination IP
INIT_NIC: ; Gets the IP from the User
	;Read two IPs (my IP first, then destination's IP)
	cmp dword [optimization_level],'CUST' ; Check the optimization level
	je .GETFROMUSR ; Optimization Level set to custom, then 
	;get it from User
	jmp INITIALIZE_PCI ; Or Directly Initialize the PCI Bus with the
	; default configuration.
.GETFROMUSR:
	mov   si,ip_prompt
	call os_print_string
    call  READ_IP
    mov dword  [my_ip],edx ; Your IP
    call  READ_IP
    mov  dword [dest_ip],edx ; Store the Destination IP in dest_ip
	jmp INITIALIZE_PCI ; Initialize the PCI Bus
INITIALIZE_PCI: ; Initialize the PCI Data Bus!
	; First we check whether PCI Bus is available for the hardware
	; that we are using. If yes, (of course) then we attempt to
	; connect to the Data Bus! No? (Probably Old Junkware) then
	; we display a message
	; Test for PCI BIOS AX = B101, INT 0x1A
	pusha
	mov ax,0xB101
	int 0x1A
	; Usually the BIOS clears AL after the PCI Check i.e. AL =! 0
	; But if this is unsupported then AL = 0
	test al,al ; Non-Zero Check
	jz .NO_PCI ; Error if AL = 0
	; EDX is set to 'PCI ' if the PCI BIOS Interface is available
	cmp edx,'PCI '
	jne .NO_PCI
	jmp .PCI_CONNECT ; Connect to a PCI Device
.PCI_CONNECT:
	; First we need to find a PCI Connected to the computer
	; We are searching for a Realtek 0x8139 NIC.
	; If none of them are found to this PC then we display a 
	; message. I would support other devices later.
	mov ax,0xB102 ; BIOS INT 0x1A PCI_CONNECT Function
	mov dx,0x10EC ; Realtek : Vendor ID
	mov cx,0x8139 ; Model Number, RTL8139
	xor si,si ; The first possible device is returned in SI.
	int 0x1A ; The interrupt!
	jc .NO_NIC ; NO NIC or unsupported.
	mov [PCI_ADDRESS],bx ; Store the Address
; BAR0 (Base Address #0)
    mov di,0x10
; We will now scan all the BAR's for an IO Space 
.SCAN_BAR:
	; Put the Device address in BX
	mov bx,[PCI_ADDRESS]
; Start Reading the PCI Configuration Area!
; BIOS Interrupt : AX = B10A INT 0x1A
; bit 1 of CX is set if an I/O Space is found.
	mov ax,0xB10A
	int 0x1A
; Oh Crap!
	jc FATAL
; I/O Space? 
	test cl,1
	jnz .BAR_FOUND ; Yep!
; No? Then Read the next one.
	add di,0x04 ; Add 4h to DI
; Already? Error ? or bug?
	cmp di,0x28
	jb .SCAN_BAR ; No? Then again read it
	jmp FATAL ; Error, Bail out
.BAR_FOUND:
	; YAY! We have found an I/O Space in the PCI Bus.
	; Time to get the relevant bits.
	and cx,0xFFF8
	mov [addr],cx
	jmp .GET_MAC ; Time to get the MAC Address!
.GET_MAC:
		mov dx,cx ; DX = Start of I/O Space
		; 6 bytes
		mov cx,6
		push cx ; Store it
		; MAC in MAC_ADDRESS
		mov di,MAC_ADDRESS
		jmp .READ_MAC ; Read the MAC Adress
.READ_MAC:
		; Get a byte from the port
		in al,dx
		; Next
		inc dx
		; Store it
		stosb
		call PRINT_HEX
		; Byte done
		dec cx
		jcxz .MAC_DONE
		; No? Print a colon (:) if we get something like 5544332211 then
		; make it like 55:44:33:22:11
		mov ah,0x0E
		mov al,':'
		int 0x10
		jmp .READ_MAC
.MAC_DONE:
	call os_print_newline
	popa 
	jmp draw_bck
.NO_PCI:
	mov si,PCIERR
	call os_print_string
	ret
.NO_NIC:
    mov si,error_NIC
	call os_print_string
	ret
READ_IP:
	; An IP Address has 4 Blocks (XX.XX.XX.XX)
	mov cx,4
	; BX = Each Block Let's empty it
	xor bx,bx
	; BX1 + BX2 + BX3 + BX4 = EDX (IP)
	; So we empty it.
	xor edx,edx
.LOOP:
	;Storing BX while we read the next character (will be destroyed by printc)
    push  bx
	; BIOS INT 0x16 : Get Input from Keyboard
	xor ax,ax
	int 0x16
	mov si,ax
	call os_print_string
	pop bx
	; Shit it.
	cmp   al,'.' ; Is it a '.'? I.E a dot which separate shitty blocks
    je    .DONE ; Yeah it is
	; No?
	;Return means, the IP is finished (originally that value is CR == 13, but
    ;printc changes it to LF == 10)
    cmp   al,10
    jz    .DONE
    ;Getting the value from its ASCII character
    sub   al,'0'
    ;Every other character being no digit is not allowed
    jb    FATAL
    cmp   al,9
    ja    FATAL
	xor   ah,ah
    ;Multiply current block value by 5 (we don't care about the higher word)
    lea   ebx,[ebx*4+ebx]
    ;Multiply by 2 -> all in all 10
    shl   bx,1
    ;Add current digit
    add   bx,ax
    ;Block value must fit into one byte
    test  bh,bh
    jnz   FATAL
    ;Read the next character
    jmp   .LOOP
.DONE:
    ;One block is done
    ;Shift IP left one byte
    shl   edx,8
    ;Set this byte
    mov   dl,bl
    ;One block done, so decrement the block counter and check if it's zero
    loop  .CONTINUE
    ;It's zero, so the user must have pressed return
    cmp   al,10
    jne   FATAL
    ;Turn IP into big endian
    bswap edx
    ;And return
    ret
.CONTINUE:
    ;Zero the block value so we can read the next block
    xor   bx,bx
    ;IP not yet finished, so the user must have input a dot
    cmp   al,'.'
    ;If yes, read the next block
    je    .LOOP
.any_error:
	mov si,error
	call os_print_string
	ret
FATAL:
	mov si,faterr
	call os_print_string
	ret
PRINT_HEX:
	push  ax
	;Higher nibble first
	shr   al,4
	call  PRINT
	pop   ax
	;Now the lower nibble
	and   al,0xF
	call  PRINT
	ret
PRINT:
	add   al,'0'
	cmp   al,'9'
	jbe  .DO
	;If the value was bigger than 9, we have to add 'A'-10 instead of '0', so
	;subtract '0' again
	add   al,'A'-('0'+10)
	ret
.DO:
	;And print the text 
	mov ah,0x0E
	int 0x10
	ret
	ip_prompt db 'Please Enter your IP followed by the Destination IP',0
	error db 'Error! Wrong Address Entered! IP RESET!',0
	PCIERR db 'PCI Not Found by PCI-BIOS',0
	error_NIC db 'No NIC or unsupported NIC found!',0
	faterr db 'FATAL PCI ERROR, PRESS ANY KEY TO QUIT!',0
	MAC_ADDRESS  times 6 db 0
	addr   dw 0
	PCI_ADDRESS dw 0
	optimization_level db 'LLLL',0
OK:
	ret
os_get_ip:
	push si
	mov si,.pre_ip
	call os_print_string
	call os_print_newline
	pop si
	ret
	.pre_ip db '10.0.2.15 (QEMU Default)',0
